﻿#pragma once

#include <learn/dll_ext.h>
#include <learn/geom/geom_bspline_curve.h>
#include <learn/geom/geom_bspline_surface.h>

#include <fstream>

namespace xi
{

namespace utils
{

constexpr int BUF_SIZE = 65536;

struct ObjTool
{
    std::vector<xi::geom::Vec3> points; // 孤立点
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3>>> splineCurves;
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineCurve<3, true>>> splineCurvesR;
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineSurface<3>>> splineSurfaces;
    std::vector<std::shared_ptr<xi::geom::Geom_BSplineSurface<3, true>>> splineSurfacesR;

  private:
    static std::vector<double> split_double(const char *buf)
    {
        static char dst[BUF_SIZE];
        std::vector<double> pos;

        // 由于空格的分布问题，倒过来读取
        for (int i = std::strlen(buf) - 1; i >= 0; i--)
        {
            if (buf[i] == ' ')
            {
                std::memcpy(dst, &buf[i + 1], std::strlen(buf) - 1 - i);
                dst[std::strlen(buf) - 1 - i] = '\0';
                pos.push_back(std::stod(dst));
            }
        }
        // 反转结果
        std::reverse(pos.begin(), pos.end());
        return pos;
    }

    static std::vector<int> split_int(const char *buf)
    {
        static char dst[BUF_SIZE];
        std::vector<int> deg;

        // 由于空格的分布问题，倒过来读取
        for (int i = std::strlen(buf) - 1; i >= 0; i--)
        {
            if (buf[i] == ' ')
            {
                std::memcpy(dst, &buf[i + 1], std::strlen(buf) - 1 - i);
                dst[std::strlen(buf) - 1 - i] = '\0';
                deg.push_back(std::stoi(dst));
            }
        }
        // 反转结果
        std::reverse(deg.begin(), deg.end());
        return deg;
    }

  public:
    /**
     * @brief 从指定路径读取 obj 文件
     *
     * @param[in] path
     */
    void read(const char *path)
    {
        static char buf[BUF_SIZE];
        std::fstream fp(path, std::ios::in);
        if (!fp)
        {
            std::cerr << "Failed to open file: " << path << std::endl;
            return;
        }

        // 保存数据
        std::vector<xi::geom::Vec3> poles;
        std::vector<double> weights;
        std::vector<std::string> rat;
        std::vector<std::vector<int>> deg;
        std::vector<std::vector<int>> index;
        std::vector<std::vector<double>> uknots;
        std::vector<std::vector<double>> vknots;

        std::string str;
        while (true)
        {
            // 排除前面的空白
            while (!fp.eof())
            {
                fp >> str;
                if (str == "v")
                    break;
            }
            if (fp.eof())
                break;

            // 记录权重
            while (str == "v" || str == "p")
            {
                // 获得坐标
                fp.getline(buf, BUF_SIZE);

                // 孤立点占位
                if (str == "p")
                    points.push_back(poles.back());
                else
                {
                    // 读取坐标和权重
                    auto coords = split_double(buf);
                    poles.emplace_back(coords[0], coords[1], coords[2]);

                    // 即使是非有理，也需要占位
                    if (coords.size() == 4)
                        weights.push_back(coords[3]);
                    else
                        weights.push_back(1);
                }

                // 获得下一行
                fp >> str;
            }

            // 获得 cstype 行，记录类型
            fp >> str;
            rat.push_back(str);
            if (str == "rat")
                fp >> str;

            // 记录 deg
            fp >> str;
            fp.getline(buf, BUF_SIZE);
            deg.push_back(split_int(buf));

            // 获得顶点索引 str == curv，还需要去掉前两个表示节点区间
            double d;
            fp >> str >> d >> d;
            if (deg.back().size() == 2)
            {
                // 曲面还需要再去掉两个 v 节点区间
                fp >> d >> d;
            }
            fp.getline(buf, BUF_SIZE);
            index.push_back(split_int(buf));

            // 获得节点 param u
            fp >> str >> str;
            fp.getline(buf, BUF_SIZE);
            uknots.push_back(split_double(buf));

            // 判断是否为曲面
            if (deg.back().size() == 2)
            {
                // 获得节点 param v
                fp >> str >> str;
                fp.getline(buf, BUF_SIZE);
                vknots.push_back(split_double(buf));
            }
            else
            {
                vknots.push_back(std::vector<double>());
            }
        }

        // 构造曲线曲面
        for (std::size_t i = 0; i < rat.size(); i++)
        {
            if (rat[i] == "bspline")
            {
                std::vector<xi::geom::Vec3> cpts(index[i].size());
                for (std::size_t j = 0; j < index[i].size(); j++)
                    cpts[j] = poles[index[i][j] - 1];

                if (deg[i].size() == 1)
                {
                    auto curve = std::make_shared<xi::geom::Geom_BSplineCurve<3>>(std::move(cpts), std::move(uknots[i]),
                                                                                  deg[i][0]);
                    splineCurves.emplace_back(curve);
                }
                else
                {
                    auto surface = std::make_shared<xi::geom::Geom_BSplineSurface<3>>(
                        std::move(cpts), std::move(uknots[i]), std::move(vknots[i]), deg[i][0], deg[i][1]);
                    splineSurfaces.emplace_back(surface);
                }
            }
            else
            {
                std::vector<xi::geom::Vec4> cpts(index[i].size());
                for (std::size_t j = 0; j < index[i].size(); j++)
                {
                    for (std::size_t k = 0; k < 3; k++)
                        cpts[j][k] = poles[index[i][j] - 1][k];
                    cpts[j][3] = weights[index[i][j] - 1];
                }

                if (deg[i].size() == 1)
                {
                    auto curve = std::make_shared<xi::geom::Geom_BSplineCurve<3, true>>(
                        std::move(cpts), std::move(uknots[i]), deg[i][0]);
                    splineCurvesR.emplace_back(curve);
                }
                else
                {
                    auto surface = std::make_shared<xi::geom::Geom_BSplineSurface<3, true>>(
                        std::move(cpts), std::move(uknots[i]), std::move(vknots[i]), deg[i][0], deg[i][1]);
                    splineSurfacesR.emplace_back(surface);
                }
            }
        }
        fp.close();
    }

    /**
     * @brief 从指定路径写入 obj 文件
     *
     * @param[in] path
     */
    void write(const char *path)
    {
        std::fstream fp(path, std::ios::out);
        if (!fp)
        {
            std::cerr << "Failed to open file: " << path << std::endl;
            return;
        }

        // // 输出控制点信息
        // for (int i = 1; i <= curve->NbPoles(); ++i)
        // {
        //     fp << "v " << curve->Pole(i).X() << " " << curve->Pole(i).Y() << " " << curve->Pole(i).Z();
        //     if (curve->IsRational())
        //         fp << " " << curve->Weight(i);
        //     fp << std::endl;
        // }

        // // 输出曲线类型、次数、节点向量
        // std::string type;
        // if (curve->IsRational())
        //     type = "cstype rat bspline";
        // else
        //     type = "cstype bspline";
        // fp << type << std::endl;
        // fp << "deg " << curve->Degree() << std::endl << "curv ";
        // fp << curve->FirstParameter() << " " << curve->LastParameter();
        // for (int i = 1; i <= curve->NbPoles(); ++i)
        // {
        //     fp << " " << i + index;
        // }
        // fp << std::endl << "parm u";
        // for (auto knot : curve->KnotSequence())
        // {
        //     fp << " " << knot;
        // }
        // fp << std::endl << "end" << std::endl;

        // // 修改顶点索引
        // index += curve->NbPoles();

        // // 输出控制点信息
        // for (int j = 1; j <= surface->NbVPoles(); j++)
        // {
        //     for (int k = 1; k <= surface->NbUPoles(); k++)
        //     {
        //         fp << "v " << surface->Pole(k, j).X() << " " << surface->Pole(k, j).Y() << " "
        //            << surface->Pole(k, j).Z();
        //         if (surface->IsURational() || surface->IsVRational())
        //             fp << " " << surface->Weight(k, j);
        //         fp << std::endl;
        //     }
        // }

        // // 输出曲线类型、次数、节点向量
        // std::string type;
        // if (surface->IsURational() || surface->IsVRational())
        //     type = "cstype rat bspline";
        // else
        //     type = "cstype bspline";
        // fp << type << std::endl;
        // fp << "deg " << surface->UDegree() << " " << surface->VDegree() << std::endl << "surf ";

        // Standard_Real U1, U2, V1, V2;
        // surface->Bounds(U1, U2, V1, V2);
        // fp << U1 << " " << U2 << " " << V1 << " " << V2;
        // for (int i = 1; i <= surface->NbUPoles() * surface->NbVPoles(); i++)
        //     fp << " " << i + index;
        // fp << std::endl << "parm u";
        // for (auto knot : surface->UKnotSequence())
        //     fp << " " << knot;
        // fp << std::endl << "parm v";
        // for (auto knot : surface->VKnotSequence())
        //     fp << " " << knot;
        // fp << std::endl << "end" << std::endl;

        // // 修改顶点索引
        // index += surface->NbUPoles() * surface->NbVPoles();
    }
};

} // namespace utils

} // namespace xi
